Load in packages

library(tidyverse)

library(cluster)    # clustering algorithms
library(factoextra) # clustering algorithms & visualization
library(FactoMineR)

library(tmap)
library(sf)
set.seed(123)

Load in data to use

usgs_soil <- readxl::read_xlsx(path = "C:/Users/stahlm/Desktop/soil_usgs/Appendix_4a_Chorizon_18Sept2013.xlsx",
                               sheet = "C_Horizon_formatted", skip = 12
                               )

Prepare data for clustering

Select variables to use in clustering

table_cluster <- usgs_soil %>% 
  select(SiteID,
         C_Al, C_Ba, C_Ca, C_C_Org, C_Fe, C_K, C_Mg, C_Na, C_Ti, C_S
         ) 

Name the rows with the SAMPLE_ID variable

table_cluster <- column_to_rownames(table_cluster, var = "SiteID")

table_cluster <- table_cluster %>% mutate_all(as.numeric)
Warning: There were 10 warnings in `mutate()`.
The first warning was:
ℹ In argument: `C_Al = .Primitive("as.double")(C_Al)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 9 remaining warnings.
table_cluster <- table_cluster %>% 
  drop_na()

Rescale the data

table_cluster_scaled <- scale(table_cluster)

PCA

res.pca <- PCA(table_cluster_scaled,  graph = FALSE)
# Control variable colors using their contributions
fviz_pca_var(res.pca, col.var="contrib",
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
             repel = TRUE # Avoid text overlapping
             )

Cluster the data

fviz_nbclust(
  table_cluster_scaled,
  pam,
  k.max = 15,
  method = "wss",
  diss = get_dist(table_cluster, method = "spearman")
  #diss = get_dist(table_cluster, method = "euclidean")
) 

n_clust = 6
#k_n <- kmeans(table_cluster_scaled, centers = n_clust, nstart = 100, iter.max = 15000)


k_n = cluster::pam(table_cluster_scaled, k = n_clust)
fig_cluster <- fviz_cluster(k_n, data = table_cluster_scaled, ellipse = T, palette = "Set2",
                            geom = "point") +
  theme_classic()
fig_cluster

Create a data frame with the pixel coordinates and the cluster assigned to that pixel

df_cluster_info <- tibble(SiteID = row.names(table_cluster_scaled),
                          cluster_id = k_n$cluster) %>% 
  mutate(SiteID = as.numeric(SiteID))
usgs_soil <- usgs_soil %>% 
  left_join(df_cluster_info)
Joining with `by = join_by(SiteID)`
usgs_soil <- usgs_soil %>% 
  mutate(cluster_id = factor(cluster_id))
usgs_soil <- usgs_soil %>% 
  mutate(across(starts_with("C_"), as.numeric))
Warning: There were 75 warnings in `mutate()`.
The first warning was:
ℹ In argument: `across(starts_with("C_"), as.numeric)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 74 remaining warnings.
usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_As, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")




usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_Fe, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")



usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_S, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")



usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_Ca, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")





usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_Tot_Carb, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")




usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_Tot_Clay, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")



usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_Tot_Carb, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")



usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_Amorph, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")




usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_Pb, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")



usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_U, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")




usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  mutate(C_C_Org = as.numeric(C_C_Org)) %>% 
  
  ggplot(aes(x = cluster_id, y = C_U, fill = cluster_id)) +
  geom_boxplot() +
  scale_fill_brewer(type = "qual", palette = "Set2") +
  
  scale_y_log10() +
  
  theme_classic() +
  theme(legend.position = "none")

summary(usgs_soil$cluster_id)
   1    2    3    4    5    6 NA's 
 957  442  835 1512  435  591   85 
sf_usgs_soil <- usgs_soil %>% 
  st_as_sf(coords = c("Longitude", "Latitude"))
tmap_mode("view")
tmap mode set to interactive viewing
sf_usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  
  tm_shape() +
  tm_dots(col = "cluster_id", palette = "Set2",
          popup.vars = c("cluster_id")
          ) +

  tm_mouse_coordinates() +
  
  tm_basemap(server = c("Esri.WorldImagery", "OpenStreetMap", "Esri.WorldShadedRelief")) +
  
  tm_scale_bar() 
Warning: Currect projection of shape . unknown. Long-lat (WGS84) is assumed.
sf_usgs_soil %>% 
  filter(!is.na(cluster_id)) %>% 
  mutate(C_C_Org = as.numeric(C_C_Org)) %>% 
  
  tm_shape() +
  tm_dots(col = "C_C_Org", palette = "viridis", style = "quantile", n = 8,
          popup.vars = c("cluster_id", "C_C_Org")
          ) +

  tm_mouse_coordinates() +
  
  tm_basemap(server = c("Esri.WorldImagery", "OpenStreetMap", "Esri.WorldShadedRelief")) +
  
  tm_scale_bar() 
Warning: Currect projection of shape . unknown. Long-lat (WGS84) is assumed.
sf_bangladesh_gw %>% 
  filter(!is.na(cluster_id)) %>% 
  
  tm_shape() +
  tm_dots(col = "As_ugL", palette = "viridis", breaks = c(0,5,10,50,100,200),
          popup.vars = c("cluster_id", "WELL_DEPTH_m", "As_ugL")
          ) +

  tm_mouse_coordinates() +
  
  tm_basemap(server = c("Esri.WorldImagery", "OpenStreetMap", "Esri.WorldShadedRelief")) +
  
  tm_scale_bar() + 
  tm_facets(by = "cluster_id", sync = T)
Warning: Currect projection of shape . unknown. Long-lat (WGS84) is assumed.Warning: Values have found that are higher than the highest break
LS0tDQp0aXRsZTogIkNsdXN0ZXIgYW5hbHlzaXMgZXhhbXBsZXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCiMjIExvYWQgaW4gcGFja2FnZXMNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmxpYnJhcnkoY2x1c3RlcikgICAgIyBjbHVzdGVyaW5nIGFsZ29yaXRobXMNCmxpYnJhcnkoZmFjdG9leHRyYSkgIyBjbHVzdGVyaW5nIGFsZ29yaXRobXMgJiB2aXN1YWxpemF0aW9uDQpsaWJyYXJ5KEZhY3RvTWluZVIpDQoNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkoc2YpDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpgYGANCg0KDQoNCiMjIExvYWQgaW4gZGF0YSB0byB1c2UNCmBgYHtyfQ0KdXNnc19zb2lsIDwtIHJlYWR4bDo6cmVhZF94bHN4KHBhdGggPSAiQzovVXNlcnMvc3RhaGxtL0Rlc2t0b3Avc29pbF91c2dzL0FwcGVuZGl4XzRhX0Nob3Jpem9uXzE4U2VwdDIwMTMueGxzeCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAiQ19Ib3Jpem9uX2Zvcm1hdHRlZCIsIHNraXAgPSAxMg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCmBgYA0KIyMgUHJlcGFyZSBkYXRhIGZvciBjbHVzdGVyaW5nDQoNCiMjIyBTZWxlY3QgdmFyaWFibGVzIHRvIHVzZSBpbiBjbHVzdGVyaW5nDQpgYGB7cn0NCnRhYmxlX2NsdXN0ZXIgPC0gdXNnc19zb2lsICU+JSANCiAgc2VsZWN0KFNpdGVJRCwNCiAgICAgICAgIENfQWwsIENfQmEsIENfQ2EsIENfQ19PcmcsIENfRmUsIENfSywgQ19NZywgQ19OYSwgQ19UaSwgQ19TDQogICAgICAgICApIA0KDQpgYGANCg0KDQojIyMgTmFtZSB0aGUgcm93cyB3aXRoIHRoZSBTQU1QTEVfSUQgdmFyaWFibGUgDQpgYGB7cn0NCnRhYmxlX2NsdXN0ZXIgPC0gY29sdW1uX3RvX3Jvd25hbWVzKHRhYmxlX2NsdXN0ZXIsIHZhciA9ICJTaXRlSUQiKQ0KDQp0YWJsZV9jbHVzdGVyIDwtIHRhYmxlX2NsdXN0ZXIgJT4lIG11dGF0ZV9hbGwoYXMubnVtZXJpYykNCg0KdGFibGVfY2x1c3RlciA8LSB0YWJsZV9jbHVzdGVyICU+JSANCiAgZHJvcF9uYSgpDQpgYGANCg0KIyMjIFJlc2NhbGUgdGhlIGRhdGENCmBgYHtyfQ0KdGFibGVfY2x1c3Rlcl9zY2FsZWQgPC0gc2NhbGUodGFibGVfY2x1c3RlcikNCmBgYA0KDQoNCiMjIFBDQQ0KYGBge3J9DQpyZXMucGNhIDwtIFBDQSh0YWJsZV9jbHVzdGVyX3NjYWxlZCwgIGdyYXBoID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQojIENvbnRyb2wgdmFyaWFibGUgY29sb3JzIHVzaW5nIHRoZWlyIGNvbnRyaWJ1dGlvbnMNCmZ2aXpfcGNhX3ZhcihyZXMucGNhLCBjb2wudmFyPSJjb250cmliIiwNCiAgICAgICAgICAgICBncmFkaWVudC5jb2xzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKSwNCiAgICAgICAgICAgICByZXBlbCA9IFRSVUUgIyBBdm9pZCB0ZXh0IG92ZXJsYXBwaW5nDQogICAgICAgICAgICAgKQ0KYGBgDQoNCg0KIyMgQ2x1c3RlciB0aGUgZGF0YQ0KYGBge3J9DQpmdml6X25iY2x1c3QoDQogIHRhYmxlX2NsdXN0ZXJfc2NhbGVkLA0KICBwYW0sDQogIGsubWF4ID0gMTUsDQogIG1ldGhvZCA9ICJ3c3MiLA0KICBkaXNzID0gZ2V0X2Rpc3QodGFibGVfY2x1c3RlciwgbWV0aG9kID0gInNwZWFybWFuIikNCiAgI2Rpc3MgPSBnZXRfZGlzdCh0YWJsZV9jbHVzdGVyLCBtZXRob2QgPSAiZXVjbGlkZWFuIikNCikgDQpgYGANCg0KDQoNCmBgYHtyfQ0Kbl9jbHVzdCA9IDYNCiNrX24gPC0ga21lYW5zKHRhYmxlX2NsdXN0ZXJfc2NhbGVkLCBjZW50ZXJzID0gbl9jbHVzdCwgbnN0YXJ0ID0gMTAwLCBpdGVyLm1heCA9IDE1MDAwKQ0KDQoNCmtfbiA9IGNsdXN0ZXI6OnBhbSh0YWJsZV9jbHVzdGVyX3NjYWxlZCwgayA9IG5fY2x1c3QpDQpgYGANCg0KDQpgYGB7cn0NCmZpZ19jbHVzdGVyIDwtIGZ2aXpfY2x1c3RlcihrX24sIGRhdGEgPSB0YWJsZV9jbHVzdGVyX3NjYWxlZCwgZWxsaXBzZSA9IFQsIHBhbGV0dGUgPSAiU2V0MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJwb2ludCIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KDQpgYGB7cn0NCmZpZ19jbHVzdGVyDQpgYGANCiMjIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggdGhlIHBpeGVsIGNvb3JkaW5hdGVzIGFuZCB0aGUgY2x1c3RlciBhc3NpZ25lZCB0byB0aGF0IHBpeGVsDQpgYGB7cn0NCmRmX2NsdXN0ZXJfaW5mbyA8LSB0aWJibGUoU2l0ZUlEID0gcm93Lm5hbWVzKHRhYmxlX2NsdXN0ZXJfc2NhbGVkKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9pZCA9IGtfbiRjbHVzdGVyKSAlPiUgDQogIG11dGF0ZShTaXRlSUQgPSBhcy5udW1lcmljKFNpdGVJRCkpDQpgYGANCg0KDQpgYGB7cn0NCnVzZ3Nfc29pbCA8LSB1c2dzX3NvaWwgJT4lIA0KICBsZWZ0X2pvaW4oZGZfY2x1c3Rlcl9pbmZvKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnVzZ3Nfc29pbCA8LSB1c2dzX3NvaWwgJT4lIA0KICBtdXRhdGUoY2x1c3Rlcl9pZCA9IGZhY3RvcihjbHVzdGVyX2lkKSkNCmBgYA0KDQpgYGB7cn0NCnVzZ3Nfc29pbCA8LSB1c2dzX3NvaWwgJT4lIA0KICBtdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCJDXyIpLCBhcy5udW1lcmljKSkNCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD0gNH0NCnVzZ3Nfc29pbCAlPiUgDQogIGZpbHRlcighaXMubmEoY2x1c3Rlcl9pZCkpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IGNsdXN0ZXJfaWQsIHkgPSBDX0FzLCBmaWxsID0gY2x1c3Rlcl9pZCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIlNldDIiKSArDQogIA0KICBzY2FsZV95X2xvZzEwKCkgKw0KICANCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQoNCg0KdXNnc19zb2lsICU+JSANCiAgZmlsdGVyKCFpcy5uYShjbHVzdGVyX2lkKSkgJT4lIA0KICANCiAgZ2dwbG90KGFlcyh4ID0gY2x1c3Rlcl9pZCwgeSA9IENfRmUsIGZpbGwgPSBjbHVzdGVyX2lkKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgDQogIHNjYWxlX3lfbG9nMTAoKSArDQogIA0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCg0KdXNnc19zb2lsICU+JSANCiAgZmlsdGVyKCFpcy5uYShjbHVzdGVyX2lkKSkgJT4lIA0KICANCiAgZ2dwbG90KGFlcyh4ID0gY2x1c3Rlcl9pZCwgeSA9IENfUywgZmlsbCA9IGNsdXN0ZXJfaWQpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9ICJTZXQyIikgKw0KICANCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KDQp1c2dzX3NvaWwgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNsdXN0ZXJfaWQpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBjbHVzdGVyX2lkLCB5ID0gQ19DYSwgZmlsbCA9IGNsdXN0ZXJfaWQpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9ICJTZXQyIikgKw0KICANCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KDQoNCg0KdXNnc19zb2lsICU+JSANCiAgZmlsdGVyKCFpcy5uYShjbHVzdGVyX2lkKSkgJT4lIA0KICANCiAgZ2dwbG90KGFlcyh4ID0gY2x1c3Rlcl9pZCwgeSA9IENfVG90X0NhcmIsIGZpbGwgPSBjbHVzdGVyX2lkKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgDQogIHNjYWxlX3lfbG9nMTAoKSArDQogIA0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCg0KDQp1c2dzX3NvaWwgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNsdXN0ZXJfaWQpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBjbHVzdGVyX2lkLCB5ID0gQ19Ub3RfQ2xheSwgZmlsbCA9IGNsdXN0ZXJfaWQpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9ICJTZXQyIikgKw0KICANCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KDQp1c2dzX3NvaWwgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNsdXN0ZXJfaWQpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBjbHVzdGVyX2lkLCB5ID0gQ19Ub3RfQ2FyYiwgZmlsbCA9IGNsdXN0ZXJfaWQpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9ICJTZXQyIikgKw0KICANCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KDQp1c2dzX3NvaWwgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNsdXN0ZXJfaWQpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBjbHVzdGVyX2lkLCB5ID0gQ19BbW9ycGgsIGZpbGwgPSBjbHVzdGVyX2lkKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgDQogIHNjYWxlX3lfbG9nMTAoKSArDQogIA0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCg0KDQp1c2dzX3NvaWwgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNsdXN0ZXJfaWQpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBjbHVzdGVyX2lkLCB5ID0gQ19QYiwgZmlsbCA9IGNsdXN0ZXJfaWQpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9ICJTZXQyIikgKw0KICANCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KDQp1c2dzX3NvaWwgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNsdXN0ZXJfaWQpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBjbHVzdGVyX2lkLCB5ID0gQ19VLCBmaWxsID0gY2x1c3Rlcl9pZCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIlNldDIiKSArDQogIA0KICBzY2FsZV95X2xvZzEwKCkgKw0KICANCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQoNCg0KdXNnc19zb2lsICU+JSANCiAgZmlsdGVyKCFpcy5uYShjbHVzdGVyX2lkKSkgJT4lIA0KICBtdXRhdGUoQ19DX09yZyA9IGFzLm51bWVyaWMoQ19DX09yZykpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IGNsdXN0ZXJfaWQsIHkgPSBDX1UsIGZpbGwgPSBjbHVzdGVyX2lkKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgDQogIHNjYWxlX3lfbG9nMTAoKSArDQogIA0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkodXNnc19zb2lsJGNsdXN0ZXJfaWQpDQpgYGANCg0KDQpgYGB7cn0NCnNmX3VzZ3Nfc29pbCA8LSB1c2dzX3NvaWwgJT4lIA0KICBzdF9hc19zZihjb29yZHMgPSBjKCJMb25naXR1ZGUiLCAiTGF0aXR1ZGUiKSkNCmBgYA0KDQoNCmBgYHtyfQ0KdG1hcF9tb2RlKCJ2aWV3IikNCg0Kc2ZfdXNnc19zb2lsICU+JSANCiAgZmlsdGVyKCFpcy5uYShjbHVzdGVyX2lkKSkgJT4lIA0KICANCiAgdG1fc2hhcGUoKSArDQogIHRtX2RvdHMoY29sID0gImNsdXN0ZXJfaWQiLCBwYWxldHRlID0gIlNldDIiLA0KICAgICAgICAgIHBvcHVwLnZhcnMgPSBjKCJjbHVzdGVyX2lkIikNCiAgICAgICAgICApICsNCg0KICB0bV9tb3VzZV9jb29yZGluYXRlcygpICsNCiAgDQogIHRtX2Jhc2VtYXAoc2VydmVyID0gYygiRXNyaS5Xb3JsZEltYWdlcnkiLCAiT3BlblN0cmVldE1hcCIsICJFc3JpLldvcmxkU2hhZGVkUmVsaWVmIikpICsNCiAgDQogIHRtX3NjYWxlX2JhcigpIA0KYGBgDQoNCg0KYGBge3J9DQpzZl91c2dzX3NvaWwgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNsdXN0ZXJfaWQpKSAlPiUgDQogIG11dGF0ZShDX0NfT3JnID0gYXMubnVtZXJpYyhDX0NfT3JnKSkgJT4lIA0KICANCiAgdG1fc2hhcGUoKSArDQogIHRtX2RvdHMoY29sID0gIkNfQ19PcmciLCBwYWxldHRlID0gInZpcmlkaXMiLCBzdHlsZSA9ICJxdWFudGlsZSIsIG4gPSA4LA0KICAgICAgICAgIHBvcHVwLnZhcnMgPSBjKCJjbHVzdGVyX2lkIiwgIkNfQ19PcmciKQ0KICAgICAgICAgICkgKw0KDQogIHRtX21vdXNlX2Nvb3JkaW5hdGVzKCkgKw0KICANCiAgdG1fYmFzZW1hcChzZXJ2ZXIgPSBjKCJFc3JpLldvcmxkSW1hZ2VyeSIsICJPcGVuU3RyZWV0TWFwIiwgIkVzcmkuV29ybGRTaGFkZWRSZWxpZWYiKSkgKw0KICANCiAgdG1fc2NhbGVfYmFyKCkgDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc2ZfYmFuZ2xhZGVzaF9ndyAlPiUgDQogIGZpbHRlcighaXMubmEoY2x1c3Rlcl9pZCkpICU+JSANCiAgDQogIHRtX3NoYXBlKCkgKw0KICB0bV9kb3RzKGNvbCA9ICJBc191Z0wiLCBwYWxldHRlID0gInZpcmlkaXMiLCBicmVha3MgPSBjKDAsNSwxMCw1MCwxMDAsMjAwKSwNCiAgICAgICAgICBwb3B1cC52YXJzID0gYygiY2x1c3Rlcl9pZCIsICJXRUxMX0RFUFRIX20iLCAiQXNfdWdMIikNCiAgICAgICAgICApICsNCg0KICB0bV9tb3VzZV9jb29yZGluYXRlcygpICsNCiAgDQogIHRtX2Jhc2VtYXAoc2VydmVyID0gYygiRXNyaS5Xb3JsZEltYWdlcnkiLCAiT3BlblN0cmVldE1hcCIsICJFc3JpLldvcmxkU2hhZGVkUmVsaWVmIikpICsNCiAgDQogIHRtX3NjYWxlX2JhcigpICsgDQogIHRtX2ZhY2V0cyhieSA9ICJjbHVzdGVyX2lkIiwgc3luYyA9IFQpDQpgYGANCg0KDQo=